#ifndef PACKETPP_TCP_REASSEMBLY
#define PACKETPP_TCP_REASSEMBLY

#include "ip_addr.h"
#include "mac_addr.h"
#include "pkt_meta_def.h"
#include "session_timer.h"
#include <list>
#include <time.h>
#include <unordered_map>

namespace secsmart_tcpip
{
struct tcphdr;

// TCP 重组模式
enum {
    TCP_REASSEMBLY_MODE_NORMAL = 0, // 正常模式，TCP 流不存在时，数据报文先缓存(审计产品优选)
    TCP_REASSEMBLY_MODE_FAST, // 快速模式，数据报文直接建立TCP连接，通知业务(防护墙产品优先)
    REASSEMBLY_MODE_MAX
};

// TCP 流类型，标识该流是否有缓存数据
enum {
    TCP_STREAM_TYPE_CACHED_DATA = 0,
    TCP_STREAM_TYPE_NOCACHED_DATA,
    TCP_STREAM_TYPE_MAX
};

// tcp 连接关闭原因
enum {
    TCP_STREAM_CLOSE_FIN = 0,  // 被fin报文删除
    TCP_STREAM_CLOSE_RST,      // 被reset报文删除
    TCP_STREAM_CLOSE_TIMEOUT,  // 超时删除
    TCP_STREAM_CLOSE_MANUALLY, // 手动删除
    TCP_STREAM_CLOSE_OVERFLOW, // 超规格被删除
    TCP_STREAM_CLOSE_MAX
};

// tcp 连接的状态
enum {
    TCP_STREAM_INIT = 0,
    TCP_STREAM_ESTABLISHED, // TCP 连接建立
    TCP_STREAM_CLOSED,      // TCP 连接关闭
    TCP_STREAM_RESET,       // TCP 连接reset
    TCP_STREAM_AGING,       // TCP 连接老化
    TCP_STREAM_STATUS_MAX
};

// tcp 半连接的状态
enum {
    TCP_HALF_STREAM_INIT = 0,
    TCP_HALF_STREAM_SYN_SENT,
    TCP_HALF_STREAM_PRE_ESTABLISHED, // 没有tcp握手，直接收到数据，
    TCP_HALF_STREAM_ESTABLISHED,
    TCP_HALF_STREAM_FIN,
    TCP_HALF_STREAM_CLOSE,
    TCP_HALF_STREAM_STATUS_MAX
};

typedef struct tcp_cfg {
    bool pkt_reassemble_enable;        // 报文重组开关
    bool pkt_stat_enable;
    uint8_t work_mode;                 // 工作模式
    uint32_t max_stream_num;           // 分片重组流的规格(默认16K)
    uint32_t max_seg_cache_num;        // 最大分片缓存数目
    uint32_t max_seg_cache_per_stream; // 每条流最大缓存分段数（默认32）
    uint32_t stream_timeout;           // tcp连接老化超时时间,单位毫秒（默认600秒 10分钟）
    uint32_t seg_cache_timeout;        // 缓存超时时间，单位毫秒
    uint32_t aging_proc_interval; // 老化执行间隔时间，单位毫秒。有报文每隔1秒执行一遍老化。
    uint32_t aging_proc_once_num; // 一次处理的老化节点数。
} tcp_cfg_t;

typedef struct tcp_statistic {
    uint32_t stream_cur_num;                 // 当前连接数
    uint32_t stream_total_num;               // 流总计
    uint32_t stream_syn_create_num;          // Syn创建的连接数
    uint32_t stream_data_create_num;         // 后续数据包创建的连接数
    uint32_t stream_close_num[TCP_STREAM_CLOSE_MAX];
    uint32_t stream_over_spec_num;           // 连接超规格
    uint32_t pkt_cache_num;                  // 当前缓存的报文数
    uint32_t pkt_cache_fail_num;             // 报文缓存失败数
    uint32_t pkt_rcv_num;                    // 收到报文总计
    uint32_t pkt_not_tcp_num;                // 非tcp报文统计
    uint32_t pkt_not_server_num;             // 非server的报文
    uint32_t pkt_cache_over_spec_num;        // 分片缓存超过整机规格
    uint32_t pkt_cache_over_stream_spec_num; // 分片缓存超过每条流的规格
    uint32_t pkt_ooo_num;                    // 乱序报文数量
    uint32_t pkt_two_way_ooo_num;            // 单方向没有乱，但两个方向乱了
    uint32_t pkt_rtns_num;                   // 重传分片统计
    uint32_t pkt_in_order_num;
    uint32_t pkt_submit_num;                 // 提交给业务的报文数统计
    uint64_t pkt_submit_bytes;               // 提交给业务的报文字节数统计
    uint64_t pkt_submit_missing_bytes;       // 提交丢失的数据统计
    uint32_t pkt_drop_num;                   // 丢包
    uint32_t pkt_syn_num;
    uint32_t pkt_fin_num;
    uint32_t pkt_rst_num;
    uint32_t pkt_no_data_num;
    uint64_t pkt_rcv_data_bytes;
    void clear() { memset(this, 0, sizeof(*this)); };
} tcp_statistic_t;

typedef struct tcp_stream_id {
    uint32_t source; // 报文来源
    uint32_t vlanid; // 在VxLan报文中使用VNI，其他报文使用vlanID。隔离时需要。
    IPAddress sip;
    IPAddress dip;
    uint16_t sport;
    uint16_t dport;
    bool operator==(const struct tcp_stream_id &id) const
    {
        return (source == id.source) && (vlanid == id.vlanid) && (sip == id.sip) && (dip == id.dip) &&
               (sport == id.sport) && (dport == id.dport);
    }
} tcp_stream_id_t;

typedef struct tcp_stream_id_hash {
    uint32_t operator()(const tcp_stream_id_t &id) const
    {
        return id.source ^ id.vlanid ^ id.sip.getHash() ^ id.dip.getHash() ^ ((id.source << 16) | id.dport);
    }
} tcp_stream_id_hash_t;

typedef struct tcp_segment {
    uint8_t *pkt; // 从ip头开始的报文，包含IP头
    uint16_t data_offset;
    uint16_t data_len;
    uint32_t seq;      // tcp 头部的seq字段
    uint32_t ack;      // tcp 头部的ack字段，数据库分析有双向依赖述求
    timeval timestamp; // 报文时间戳
    tcp_segment(tcphdr *hdr, packet_meta_t *meta, int pre_overlap_len, int next_overlap_len);
    ~tcp_segment();
} tcp_segment_t;

// tcp half stream，单个方向上的半连接
typedef struct tcp_hstream {
    uint8_t state; // 连接状态
    uint8_t rcv_fin : 1,
            to_server : 1,
            reserved : 6;
    uint32_t fin_seq;
    uint32_t seq;     // 该流下一个发送报文的序列号
    uint32_t ack;     // 该流期待收到的序列号（这个字段好像没用，暂时预留）
    std::list<tcp_segment_t *> seg_list;
    uint32_t seg_num; // 缓存的分段数。由于list.size()接口性能太差，链表的分段数自己统计
    tcp_hstream();
} tcp_hstream_t;

#ifdef LIBDEBUG
extern int g_tcp_stream_new_num;
extern int g_tcp_stream_delete_num;
#endif
/**
 * Tcp 数据流结构，记录tcp stream相关的所有信息
 */
typedef struct tcp_stream {
#ifdef LIBDEBUG
    tcp_stream() { g_tcp_stream_new_num++; }
    ~tcp_stream() { g_tcp_stream_delete_num++; }
#endif
    tcp_stream_id_t id;
    tcp_hstream_t client;
    tcp_hstream_t server;
    MacAddress smac;
    MacAddress dmac;
    IPAddress out_sip;
    IPAddress out_dip;
    timeval start_time;              // 报文中的时间
    timeval end_time;                // 报文中的时间
    time_t update_time;              // tcp stream 更新时间，用于老化
    int status;                      // tcp stream的状态
    tcp_statistic_t statistic;
    bool reversed;                   // 表示client和server反了
    bool cache_flag;                 // 标识该tcp流是否有缓存数据
    bool rcv_syn;                    // 收到syn报文
    bool in_tunnel;                  // 隧道流量
    std::shared_ptr<void> session;   // session指针
    session_timer_node_t timer_node; // 定时器节点

    bool is_in_tunnel() const { return in_tunnel; }
    uint32_t get_source() const { return id.source; }
    MacAddress get_smac() const { return reversed ? dmac : smac; }
    MacAddress get_dmac() const { return reversed ? smac : dmac; }
    uint32_t get_vlanid() const { return id.vlanid; }
    IPAddress get_sip() const { return reversed ? id.dip : id.sip; }
    IPAddress get_dip() const { return reversed ? id.sip : id.dip; }
    uint16_t get_sport() const { return reversed ? id.dport : id.sport; }
    uint16_t get_dport() const { return reversed ? id.sport : id.dport; }
    IPAddress get_outsip() const { return reversed ? out_dip : out_sip; }
    IPAddress get_outdip() const { return reversed ? out_sip : out_dip; }
    timeval get_start_time() const { return start_time; }
    timeval get_end_time() const { return end_time; }
} tcp_stream_t;

typedef std::shared_ptr<tcp_stream_t> tcp_stream_sptr;

/**
 * 上送给上层数据的结构
 */
typedef struct tcp_stream_data {
    uint8_t *data;            // tcp 载荷数据起始地址
    size_t data_len;          // tcp 载荷数据长度
    size_t data_offset;       // tcp 载荷数据对于IP头的偏移量， data-data_offset为ip头的地址
    size_t snd_missing_bytes; // 数据发送方式丢失数据
    size_t rcv_missing_bytes; // 数据接收方向丢失数据
    bool is_forward;          // 是否是正向报文,和连接方向一致的报文
    timeval timestamp;        // 抓包报文中的时间戳
} tcp_stream_data_t;

/**
 * @brief tcp 事件回调结构
 */
typedef struct tcp_conn_event_cb {
    /*
     * @brief tcp连接建立回调接口。
     * @param[in] conn: tcp 连接信息
     * @param[in] user_data: 上层业务注册时带的业务数据
     * @return:基于tcp连接的上层用户数据，收到报文和结束连接的回调中会用将该信息带给上层应用
     */
    std::shared_ptr<void> (*tcp_conn_event_start)(const tcp_stream_sptr conn, void *user_data);

    /*
     * @brief 收到tcp数据报文的回调接口。
     * @param[in] conn: tcp 连接信息
     * @param[in] data: tcp 连接收到的报文数据
     * @param[in] usr_conn_data: tcp 连接建立的返回值
     * @param[in] user_data: 上层业务注册时带的业务数据
     */
    void (*tcp_conn_event_rcv_data)(const tcp_stream_sptr conn, const tcp_stream_data_t *data,
                                    std::shared_ptr<void> usr_conn_data, void *usr_data);

    /*
     * @brief 收到tcp控制报文的回调接口。当前仅支持syn报文的上报，其他控制报文不支持上报。（为ndpl提供的接口）
     * @param[in] conn: tcp 连接信息
     * @param[in] data: tcp 连接收到的报文数据
     * @param[in] usr_conn_data: tcp 连接建立的返回值
     * @param[in] user_data: 上层业务注册时带的业务数据
     */
    void (*tcp_conn_event_rcv_ctrl_pkt)(const tcp_stream_sptr conn, const tcp_stream_data_t *data,
                                        std::shared_ptr<void> usr_conn_data, void *usr_data);

    /*
     * @brief tcp连接拆除回调接口。
     * @param[in] conn: tcp 连接信息
     * @param[in] close_reason: tcp 连接关闭原因
     *  TCP_STREAM_CLOSE_FIN = 0,  // 被fin报文删除
     *  TCP_STREAM_CLOSE_RST,      // 被reset报文删除
     *  TCP_STREAM_CLOSE_TIMEOUT,  // 超时删除
     *  TCP_STREAM_CLOSE_MANUALLY, // 手动删除
     *  TCP_STREAM_CLOSE_OVERFLOW, // 超规格被删除
     * @param[in] usr_conn_data: tcp 连接建立的返回值
     * @param[in] user_data: 上层业务注册时带的业务数据
     */
    void (*tcp_conn_event_end)(const tcp_stream_sptr conn, uint32_t close_reason, std::shared_ptr<void> usr_conn_data,
                               void *usr_data);
} tcp_conn_event_cb_t;

/**
 * @class TcpPacketHandler
 */
class TcpPacketHandler
{
  public:
    enum {
        TCP_PKT_OK = 0,
        TCP_PKT_ERR,
        TCP_PKT_CACHE,
    };

    TcpPacketHandler();

    ~TcpPacketHandler();

    // 注册上层业务的回调接口，在tcp链接建立、收到数据、结束连接时，会调用注册的接口。
    void register_tcp_conn_event_cb(tcp_conn_event_cb_t cb, void *user_data);

    /*
     * @brief 对TCP报文进行处理
     * @param tcp_hdr: 报文的tcp头
     * @param meta 原始报文的基本信息
     * @return TCP_PKT_OK / TCP_PKT_ERR / TCP_PKT_CACHE
     */
    uint32_t process_packet(uint8_t *tcp_hdr, packet_meta_t *meta);

    void close_tcp_stream(tcp_stream_id_t &id);

    void close_all_tcp_streams();

    bool is_stream_open(const tcp_stream_id_t &id);

    void print_tcp_stream_statistic();

    // 修改模块配置参数
    void set_cfg(tcp_cfg_t &cfg) { m_cfg = cfg; }
    void set_cfg_reassemble_enable(bool enable) { m_cfg.pkt_reassemble_enable = enable; }
    void set_cfg_max_stream_num(uint32_t max_num) { m_cfg.max_stream_num = max_num; }
    void set_cfg_max_seg_cache_num(uint32_t max_num) { m_cfg.max_seg_cache_num = max_num; }
    void set_cfg_stream_timeout( uint32_t timeout) { m_cfg.stream_timeout = timeout; }

    // 获取模块配置参数
    tcp_cfg_t *get_cfg() { return &m_cfg; }

    // 获取模块统计
    tcp_statistic_t *get_statistic() { return &m_stat; }

  private:
    typedef std::pair<std::list<tcp_stream_sptr>::iterator, tcp_stream_sptr> tcp_stream_map_value_pair;
    typedef std::unordered_map<tcp_stream_id_t, tcp_stream_map_value_pair, tcp_stream_id_hash_t> tcp_stream_map;
    typedef std::list<tcp_stream_sptr> tcp_stream_list;

    tcp_stream_map m_tcp_stream_map;
    tcp_stream_list m_tcp_stream_list; // 按时间排序(前面的节点新，后面的节点旧)，用于超规格时删除最老的stream
    uint64_t m_aging_last_proc_time;   // 上次老化处理时间

    // 挂接外部模块接口，将重组后的数据提交给外部模块
    tcp_conn_event_cb_t m_tcp_event_cb;
    void *m_user_data;

    tcp_cfg_t m_cfg;
    tcp_statistic_t m_stat;

    // tcp 数据流的创建和删除
    void get_tcp_streamid(packet_meta_t *meta, tcp_stream_id_t &id);
    tcp_stream_sptr get_tcp_stream(tcp_stream_id_t &id);
    void init_tcp_stream(tcp_stream_sptr stream, tcp_stream_id_t &id, tcphdr *hdr, packet_meta_t *meta);
    tcp_stream_sptr add_tcp_stream(tcp_stream_id_t &id, tcphdr *hdr, packet_meta_t *meta);
    void del_tcp_stream(tcp_stream_id_t &id, uint32_t reason);
    void del_oldest_tcp_stream();
    void del_all_tcp_streams();
    
    // tcp 数据段缓存处理
    uint32_t cache_tcp_segment(tcp_stream_sptr stream, tcp_hstream_t *halfConn, tcphdr *hdr, packet_meta_t *meta);
    void del_one_cached_segment(tcp_stream_sptr stream);
    void del_all_cached_segment(tcp_stream_sptr stream);

    uint32_t flush_cached_tcp_segment_oneway(tcp_stream_sptr stream, tcp_hstream_t *snd, tcp_hstream_t *rcv,
                                             bool force = false);
    void flush_cached_tcp_segment(tcp_stream_sptr stream);

    // tcp 数据流报文处理
    uint32_t first_pkt_proc(tcphdr *hdr, packet_meta_t *meta, tcp_stream_id_t &id);
    uint32_t subsequent_pkt_proc(tcp_stream_sptr stream, tcphdr *hdr, packet_meta_t *meta);
    uint32_t tcp_stream_data_proc(tcp_stream_sptr stream, tcp_hstream_t *snd, tcp_hstream_t *rcv, tcphdr *hdr,
                                  packet_meta_t *meta);
    uint32_t tcp_stream_data_proc_no_reassemble(tcp_stream_sptr stream, tcp_hstream_t *snd, tcp_hstream_t *rcv,
                                                tcphdr *hdr, packet_meta_t *meta);
    uint32_t half_stream_init_state_proc(tcp_stream_sptr stream, tcp_hstream_t *snd, tcp_hstream_t *rcv, tcphdr *hdr,
                                         packet_meta_t *meta);
    uint32_t half_stream_syn_state_proc(tcp_stream_sptr stream, tcp_hstream_t *snd, tcp_hstream_t *rcv, tcphdr *hdr,
                                        packet_meta_t *meta);
    uint32_t half_stream_preest_state_proc(tcp_stream_sptr stream, tcp_hstream_t *snd, tcp_hstream_t *rcv, tcphdr *hdr,
                                           packet_meta_t *meta);
    uint32_t half_stream_est_state_proc(tcp_stream_sptr stream, tcp_hstream_t *snd, tcp_hstream_t *rcv, tcphdr *hdr,
                                        packet_meta_t *meta);
    uint32_t half_stream_fin_state_proc(tcp_stream_sptr stream, tcp_hstream_t *snd, tcp_hstream_t *rcv, tcphdr *hdr,
                                        packet_meta_t *meta);
    uint32_t half_stream_close_state_proc(tcp_stream_sptr stream, tcp_hstream_t *snd, tcp_hstream_t *rcv, tcphdr *hdr,
                                          packet_meta_t *meta);

    // 对外提交数据的内部接口
    void tcp_stream_rcv_data(tcp_stream_sptr stream, const tcp_stream_data_t &tcpData);
    void tcp_stream_start(tcp_stream_sptr stream);
    void tcp_stream_end(tcp_stream_sptr stream, uint32_t reason);
    void tcp_stream_rcv_ctrl_pkt(tcp_stream_sptr stream, tcphdr *hdr, packet_meta_t *meta);

    // 老化处理
    void aging_proc();
    void tcp_stream_timer_proc(const session_timer_node_t &node);
    void tcp_stream_add_timer(tcp_stream_sptr stream);
    void tcp_stream_update_timer(tcp_stream_sptr stream);

    // 打印统计信息
    void printTcpStreamCount();
    void printTcpPktCount(tcp_statistic_t &stat);
    void printTcpStreamPktCount(tcp_statistic_t &stat);
    void printTcpStream(tcp_stream_sptr stream);
    void printCurTcpStreamMap();
};
} // namespace secsmart_tcpip

#endif /* PACKETPP_TCP_REASSEMBLY */
